/* Sentinel - IRC Statistical and Operator Services
** stats.c - Statistical information backend functions
**
** Copyright W. Campbell and others.  See README for more details
** Some code Copyright: Jonathan George, Kai Seidler, ircd-hybrid Team,
**                      IRCnet IRCD developers.
**
** $Id: stats.c,v 1.71 2004/01/06 15:12:50 wcampbel Exp $
*/

#include "stats.h"
#include "struct.h"
#include "proto.h"
#include "s_string.h"
#include "slog.h"
#include "mem.h"

dlink_list shead;
static struct Stats *new_stats(const char *);

void IncreaseOpers(struct Server *s)
{
  debug("Increasing OperCount for %s", s->name);
  s->opers++;
  netinfo.opers++;
  if (s->opers > s->stats->maxopers)
  {
    s->stats->maxopers = s->opers;
    s->stats->t_maxopers = CurrentTime;
  }
  if (netinfo.opers > netinfo.maxopers)
  {
    netinfo.maxopers = netinfo.opers;
    netinfo.t_maxopers = CurrentTime;
  }
  if (netinfo.opers > daily.opers)
  {
    daily.opers = netinfo.opers;
    daily.t_opers = CurrentTime;
  }
}

void IncreaseAway(struct Server *s)
{
  netinfo.away++;

  if (s == NULL)
  {
    debug("Increasing AWAY count with a NULL server");
    return ;
  }
  debug("Increasing AWAY count for server: %s", s->name);

  s->away++;
  if (s->away > s->stats->maxaway)
  {
    s->stats->maxaway = s->away;
    s->stats->t_maxaway = CurrentTime;
  }
  if (netinfo.away > netinfo.maxaway)
  {
    netinfo.maxaway = netinfo.away;
    netinfo.t_maxaway = CurrentTime;
  }
}

/* Create a new Stats struct for a server we haven't seen before */
static struct Stats *new_stats(const char *name)
{
  struct Stats *s = smalloc(sizeof(struct Stats));
  dlink_node *dl = dlink_create();

  debug("new_stats(%s)", name);

  memcounts.stats++;
  strlcpy(s->name, name, MAXHOST);
  /* Only times need to be set here, since calloc initializes
  ** memory to 0
  */
  s->t_maxusers = CurrentTime;
  s->t_maxopers = CurrentTime;

  s->t_maxaway = CurrentTime;
  s->lastseen = CurrentTime;
  s->starttime = CurrentTime;

  dlink_add(s, dl, &shead);
  return s;
}

void AddStats(struct Server *s)
{
  struct Stats *st = findstats(s->name);

  debug("AddStats(%s)", s->name);

  if (!st)
  {
    st = new_stats(s->name);
    s->stats = st;
  }
  else
  {
    s->stats = st;
    st->lastseen = CurrentTime;
  }
}

struct Stats *findstats(char *name)
{
  struct Stats *t;
  dlink_node *dl;

  debug("findstats(%s)", name);
  DLINK_FOREACH(dl, shead.head)
  {
    t = (struct Stats *) dl->data;
    if (!strcasecmp(name, t->name))
      return t;
  }
  return NULL;
}

void SaveStats()
{
  FILE *fp = fopen(STATS_DB, "w");
  struct Stats *s;
  dlink_node *dl;

  if (!fp)
  {
    slog(DEFAULT_LOG, L_CRIT, "Unable to open %s for writing (%s).", STATS_DB,
         strerror(errno));
    return ;
  }

  DLINK_FOREACH(dl, shead.head)
  {
    s = (struct Stats *) dl->data;
    debug("Writing statistics to database for %s", s->name);

    fprintf(fp, "%s %d %ld %ld %d %ld %ld %ld %d %d 0 0 %d %ld\n",
            s->name, s->numsplits, s->maxusers, (long)s->t_maxusers,
            s->maxopers, (long)s->t_maxopers, (long)s->lastseen,
            (long)s->starttime, s->operkills, s->serverkills,
            s->maxaway, (long)s->t_maxaway);
  }
  fclose(fp);

  if ((fp = fopen(NSTATS_DB, "w")) == NULL)
  {
    slog(DEFAULT_LOG, L_CRIT, "Unable to open %s for writing (%s).", NSTATS_DB,
         strerror(errno));
    return ;
  }
  fprintf(fp, "%d %ld %d %ld %ld %ld 0 0 %d %ld\n", netinfo.maxopers,
          netinfo.maxusers, netinfo.maxservers, (long)netinfo.t_maxopers,
          (long)netinfo.t_maxusers, (long)netinfo.t_maxservers,
          netinfo.maxaway, (long)netinfo.t_maxaway);

  fclose(fp);
}

/* NOTE that if the format of the statistics database is incorrect.  Sentinel
** WILL crash.  I hope you kept backups...
*/
void LoadStats()
{
  FILE *fp = fopen(NSTATS_DB, "r");
  struct Stats *s;
  dlink_node *dl;
  char buffer[BUFSIZE];
  char *parv[MAXPARA];
  int parc = 0;
  int i;

  /* Clear the parv */
  for (i = 0; i < MAXPARA; i++)
    parv[i] = NULL;

  if (fp)
  {
    while (fgets(buffer, BUFSIZE, fp))
    {
      parc = msgtoken(buffer, parv);
      netinfo.maxopers = atoi(parv[0]);
      netinfo.maxusers = atol(parv[1]);
      netinfo.maxservers = atoi(parv[2]);
      netinfo.t_maxopers = atol(parv[3]);
      netinfo.t_maxusers = atol(parv[4]);
      netinfo.t_maxservers = atol(parv[5]);

      if (parv[8])
        netinfo.maxaway = atoi(parv[8]);
      else
        netinfo.maxaway = 0;
      if (parv[9])
        netinfo.t_maxaway = atol(parv[9]);
      else
        netinfo.t_maxaway = CurrentTime;
    }
    fclose(fp);
  }

  if ((fp = fopen(STATS_DB, "r")) == NULL)
    return ;

  /* Clear the parv */
  for (i = 0; i < MAXPARA; i++)
    parv[i] = NULL;

  memset(buffer, '\0', BUFSIZE);
  while (fgets(buffer, BUFSIZE, fp))
  {
    s = smalloc(sizeof(struct Stats));
    memcounts.stats++;
    parc = msgtoken(buffer, parv);

    strlcpy(s->name, parv[0], MAXHOST);
    s->numsplits = atoi(parv[1]);
    s->maxusers = atol(parv[2]);
    s->t_maxusers = atol(parv[3]);
    s->maxopers = atoi(parv[4]);
    s->t_maxopers = atol(parv[5]);
    s->lastseen = atol(parv[6]);
    s->starttime = atol(parv[7]);
    s->operkills = atoi(parv[8]);
    s->serverkills = atoi(parv[9]);

    if (parv[12])
      s->maxaway = atoi(parv[12]);
    if (parv[13])
      s->t_maxaway = atol(parv[13]);
    else
      s->t_maxaway = CurrentTime;

    debug("LoadStats(): Loaded statistics for %s", s->name);
    dl = dlink_create();
    dlink_add(s, dl, &shead);
  }
  fclose(fp);
}

